//给你一个整数数组 nums ，请你找出一个具有最大和的连续子数组（子数组最少包含一个元素），返回其最大和。 
//
// 子数组 是数组中的一个连续部分。 
//
// 
//
// 示例 1： 
//
// 
//输入：nums = [-2,1,-3,4,-1,2,1,-5,4]
//输出：6
//解释：连续子数组 [4,-1,2,1] 的和最大，为 6 。
// 
//
// 示例 2： 
//
// 
//输入：nums = [1]
//输出：1
// 
//
// 示例 3： 
//
// 
//输入：nums = [5,4,-1,7,8]
//输出：23
// 
//
// 
//
// 提示： 
//
// 
// 1 <= nums.length <= 10⁵ 
// -10⁴ <= nums[i] <= 10⁴ 
// 
//
// 
//
// 进阶：如果你已经实现复杂度为 O(n) 的解法，尝试使用更为精妙的 分治法 求解。 
// Related Topics 数组 分治 动态规划 👍 4769 👎 0

package leetcode.editor.cn;

class MaximumSubarray {
    public static void main(String[] args) {
        Solution solution = new MaximumSubarray().new Solution();
        solution.maxSubArray(new int[]{-2, 1, -3, 4, -1, 2, 1, -5, 4});
    }

    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        /**
         * 贪心法（O（n）时间复杂度）
         *
         * @param nums
         * @return
         */
//        public int maxSubArray(int[] nums) {
//            int result = Integer.MIN_VALUE; // 表示最大的结果集
//            int count = 0;  // 总和
//            for (int i = 0; i < nums.length; i++) {
//                count += nums[i];
//                if (count > result) {
//                    result = count;
//                }
//
//                // 贪心贪的地方：当总和小于0时，此时和为负数只会减少后面总和大小，所以需要重新计算连续和
//                if (count < 0) {
//                    count = 0;
//                }
//            }
//            return result;
//        }

        /**
         * 动态规划
         *
         * @param nums
         * @return
         */
    /*public int maxSubArray(int[] nums) {
        if (nums.length == 1) {
            return nums[0];
        }
        // dp[i]：包括下标i之前的最大连续子序列和为dp[i]
        int[] dp = new int[nums.length];
        dp[0] = nums[0];
        int result = dp[0];

        for (int i = 1; i < nums.length; i++) {
//                dp[i]只有两个方向可以推出来：
//                dp[i - 1] + nums[i]，即：nums[i]加入当前连续子序列和
//                nums[i]，即：从头开始计算当前连续子序列和
//                一定是取最大的，所以dp[i] = max(dp[i - 1] + nums[i], nums[i]);
            dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]);
            if (dp[i] > result) result = dp[i]; // 取出最大的
        }

        return result;
    }*/

        // 空间压缩
        /*public int maxSubArray(int[] nums) {
            int n = nums.length;
            int dp_0 = nums[0], dp_1 = 0;
            int res = dp_0;
            for (int i = 1; i < n; i++) {
                dp_1 = Math.max(dp_0 + nums[i], nums[i]);
                dp_0 = dp_1;
                res = Math.max(res, dp_1);
            }

            return res;
        }*/

        // 前缀技巧解题
        public int maxSubArray(int[] nums) {
            int n = nums.length;
            int[] preSum = new int[n + 1];
            preSum[0] = 0;
            for (int i = 0; i < n; i++) {
                preSum[i + 1] = preSum[i] + nums[i];
            }

            int res = Integer.MIN_VALUE;
            int minVal = Integer.MAX_VALUE; // 前面最小的前缀和
            for (int i = 0; i < n; i++) {
                minVal = Math.min(minVal, preSum[i]);
                res = Math.max(preSum[i + 1] - minVal, res);
            }

            return res;
        }
    }
//leetcode submit region end(Prohibit modification and deletion)

}
